# Tests building and running a GUID Partition Table (GPT) appliance image.
# "Appliance" here means that the image does not contain the normal NixOS
# infrastructure of a system profile and cannot be re-built via
# `nixos-rebuild`.

{ lib, ... }:

let
  rootPartitionLabel = "root";

  imageId = "nixos-appliance";
  imageVersion = "1-rc1";
in
{
  name = "appliance-gpt-image";

  meta.maintainers = with lib.maintainers; [ nikstur ];

  nodes.machine =
    {
      config,
      lib,
      pkgs,
      ...
    }:
    {

      imports = [ ../modules/image/repart.nix ];

      virtualisation.directBoot.enable = false;
      virtualisation.mountHostNixStore = false;
      virtualisation.useEFIBoot = true;

      # Disable boot loaders because we install one "manually".
      # TODO(raitobezarius): revisit this when #244907 lands
      boot.loader.grub.enable = false;

      system.image.id = imageId;
      system.image.version = imageVersion;

      virtualisation.fileSystems = lib.mkForce {
        "/" = {
          device = "/dev/disk/by-partlabel/${rootPartitionLabel}";
          fsType = "ext4";
        };
      };

      image.repart = {
        name = "appliance-gpt-image";
        # OVMF does not work with the default repart sector size of 4096
        sectorSize = 512;
        partitions = {
          "esp" = {
            contents =
              let
                efiArch = config.nixpkgs.hostPlatform.efiArch;
              in
              {
                "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
                  "${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";

                "/EFI/Linux/${config.system.boot.loader.ukiFile}".source =
                  "${config.system.build.uki}/${config.system.boot.loader.ukiFile}";
              };
            repartConfig = {
              Type = "esp";
              Format = "vfat";
              # Minimize = "guess" seems to not work very well for vfat
              # partitions. It's better to set a sensible default instead. The
              # aarch64 kernel seems to generally be a little bigger than the
              # x86_64 kernel. To stay on the safe side, leave some more slack
              # for every platform other than x86_64.
              SizeMinBytes = if config.nixpkgs.hostPlatform.isx86_64 then "64M" else "96M";
            };
          };
          "swap" = {
            repartConfig = {
              Type = "swap";
              Format = "swap";
              SizeMinBytes = "10M";
              SizeMaxBytes = "10M";
            };
          };
          "root" = {
            storePaths = [ config.system.build.toplevel ];
            repartConfig = {
              Type = "root";
              Format = config.fileSystems."/".fsType;
              Label = rootPartitionLabel;
              Minimize = "guess";
            };
          };
        };
      };
    };

  testScript =
    { nodes, ... }:
    ''
      import os
      import subprocess
      import tempfile

      tmp_disk_image = tempfile.NamedTemporaryFile()

      subprocess.run([
        "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img",
        "create",
        "-f",
        "qcow2",
        "-b",
        "${nodes.machine.system.build.image}/${nodes.machine.image.repart.imageFile}",
        "-F",
        "raw",
        tmp_disk_image.name,
      ])

      # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image.
      os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name

      with subtest("/etc/os-release contains the right fileds"):
        os_release = machine.succeed("cat /etc/os-release")
        t.assertIn('IMAGE_ID="${imageId}"', os_release)
        t.assertIn('IMAGE_VERSION="${imageVersion}"', os_release)

      with subtest("Bootctl reports the right boot loader type"):
        bootctl_status = machine.succeed("bootctl status")
        print(bootctl_status)
        t.assertIn("Boot Loader Specification Type #2", bootctl_status)
    '';
}
